// Uses the Linux I2C API to send and receive data from a eeprom.
// NOTE: The eeprom's control mode must be "Serial / I2C / USB".
// NOTE: For reliable operation on a Raspberry Pi, enable the i2c-gpio
//   overlay and use the I2C device it provides (usually /dev/i2c-3).
// NOTE: You might need to change the 'const char * device' line below
//   to specify the correct I2C device.
// NOTE: You might need to change the `const uint8_t address' line below
// to match the device number of your eeprom.
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>

#include <fcntl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <unistd.h>

// Opens the specified I2C device.  Returns a non-negative file descriptor
// on success, or -1 on failure.
int open_i2c_device(const char * device)
{
  int fd = open(device, O_RDWR);
  if (fd == -1)
  {
    perror(device);
    return -1;
  }
  return fd;
}
 

// Sets the target position, returning 0 on success and -1 on failure.
// @fd: The file descriptor
// @address: The target slave address
// @reg: The address of the read data
// @buffer: read data buffer
// @length: read data length
// @retval: 0 for success, -1 for failure.
int eeprom_data_nwrite(int fd, uint8_t address, uint16_t reg, 
	uint8_t *buffer, uint32_t length)
{
	uint8_t *command;
  
	command = (uint8_t*)malloc(length+sizeof(uint16_t));
	memset(command, 0, length+sizeof(uint16_t));
	command[0] = (reg>>8)&0xff;
	command[1] = reg&0xff;
	memcpy(command+2, buffer, length);

	struct i2c_msg message = { address, 0, length+sizeof(uint16_t), command};
	struct i2c_rdwr_ioctl_data ioctl_data = { &message, 1 };
	int result = ioctl(fd, I2C_RDWR, &ioctl_data);
	if (result != 1)
	{
		perror("failed to set target position");
		free(command);
		return -1;
	}
	free(command);
	return 0;
}
 
// Gets one or more variables from the eeprom
// @fd: The file descriptor
// @address: The target slave address
// @reg: The address of the read data
// @buffer: read data buffer
// @length: read data length
// @retval: 0 for success, -1 for failure.
int eeprom_data_nread(int fd, uint8_t address, uint16_t reg,
	uint8_t * buffer, uint8_t length)
{
	uint8_t command[2] = {0};
	struct i2c_msg messages[] = {
	{ address, 0, sizeof(command), command },
	{ address, I2C_M_RD, length, buffer},
	};

	struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

	command[0] = (reg>>8)&0xFF;
	command[1] = reg&0xFF;

	int result = ioctl(fd, I2C_RDWR, &ioctl_data);
	if (result != 2)
	{
		perror("failed to get variables");
		return -1;
	}
	return 0;
}
 

/*
 * file_execute	file_ops	   slave_addr
 * at24c02_test /dev/myat24c02 50
 * at24c02_test /dev/myat24c02 50
 */

int main(int argc, char **argv)
{
	uint8_t read_buf[2], write_buf[2];
	uint8_t count = 0;
	uint8_t slave_addr = 0, tmp_addr;
	int ret = 0;

	if ((argc != 3))
	{
		printf("Usage: %s <dev> <slave-addr>\n", argv[0]);
		return -1;
	}

	int fd = open_i2c_device(argv[1]);
	if (fd < 0)
	{
		printf(" can not open %s\n", argv[1]);
		return -1;
	}
	tmp_addr = atoi(argv[2]);
	slave_addr |= (tmp_addr/10)<<4 | (tmp_addr%10);
	// loop 
	for(;;) {
		if(count >= 255)
			count = 0;

		write_buf[0] = count; // reg data
		eeprom_data_nwrite(fd, slave_addr, count, write_buf, 1);
		usleep(100000);
		eeprom_data_nread(fd, slave_addr, count, read_buf, 1);

		if(read_buf[0] == write_buf[0])
			printf("eeprom@%#x : read[%#x] = write[%#x], count = %d\n", slave_addr, read_buf[0], write_buf[0], count);
		else
			printf("eeprom@%#x : read[%#x] != write[%#x], count = %d\n", slave_addr, read_buf[0], write_buf[0], count);
		
		memset(read_buf, 0, 2);
		count++;
		sleep(1);
	}


	

	close(fd);
	return 0;
}